package com.example.base_model.other

import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.withTimeout
import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject

/**
 * 表示命令执行成功的结果。
 * @param Response 响应数据的类型。
 * @param Command 命令对象的类型。
 * @param key 设备标识符。
 * @param command 发送的命令对象。
 * @param response 接收到的响应数据。
 */
data class CommandSuccess<Response, Command>(val key: String, val command: Command, val response: Response)

/**
 * 表示命令执行失败的结果。
 * @param Command 命令对象的类型。
 * @param key 设备标识符。
 * @param command 发送的命令对象。
 */
data class CommandFailure<Command>(val key: String, val command: Command)

/**
 * 表示命令执行的结果，可以是成功或失败。
 * @param Response 响应数据的类型。
 * @param Command 命令对象的类型。
 * @param success 如果命令成功，则为 [CommandSuccess] 对象，否则为 null。
 * @param failure 如果命令失败，则为 [CommandFailure] 对象，否则为 null。
 */
data class CommandResult<Response, Command>(val success: CommandSuccess<Response, Command>?, val failure: CommandFailure<Command>?)

/**
 * @Author jiangKunKun
 * @CreateDate 2025/10/3
 * @Description 管理向设备发送消息、接收响应以及超时处理。
 * @SpittingGrooves
 */
class DeviceCommandManager @Inject constructor(
    val moshi: Moshi
){
    /**
     * 用于存储待处理命令的 [MutableSharedFlow] 实例的映射。
     * 键是设备标识符，值是发射 [CommandResult] 的 Flow。
     */
    val pendingFlows = ConcurrentHashMap<String, MutableSharedFlow<CommandResult<String, String>>>()

    /**
     * 存储已发送的命令对象（JSON 字符串格式），以设备标识符作为键。
     * 当收到响应时，用于检索原始命令。
     */
    val sentCommandObjects = ConcurrentHashMap<String, String>()

    /**
     * 存储原始命令对象类型的简单名称，以设备标识符作为键。
     * 这对于调试可能很有用。
     */
    val sentCommandTypes = ConcurrentHashMap<String, String>()

    /**
     * 以 Flow 的形式发送命令并返回响应流。
     *
     * 命令以 [String] 类型发送。响应也预期为 [String] 类型。
     *
     * @param key 设备标识符。
     * @param command 要发送的命令，作为 JSON 字符串。
     * @param timeout 等待响应的最长时间（毫秒）。
     * @return 发射 [CommandResult] 的 [Flow]，其中包含命令的成功或失败信息。
     */
    fun sendCommandAsFlow(
        key: String,
        command: String,
        timeout: Long
    ): Flow<CommandResult<String, String>> = flow {
        val responseFlow = MutableSharedFlow<CommandResult<String, String>>(replay = 1)
        pendingFlows[key] = responseFlow
        sentCommandObjects[key] = command

        withTimeout(timeout) {
            responseFlow
                .take(1) // 只关心第一个响应
                .collect { response ->
                    emit(response)
                }
        }
    }.onCompletion {
        // 当 Flow 完成（成功或出现错误）时清理资源
        pendingFlows.remove(key)
        sentCommandObjects.remove(key)
        sentCommandTypes.remove(key)
    }.catch { e ->
        // 处理异常，特别是 TimeoutCancellationException
        sentCommandObjects.remove(key)
        sentCommandTypes.remove(key)
        if (e is TimeoutCancellationException) {
            // 如果发生超时，则发射 CommandFailure
            emit(CommandResult(null, CommandFailure(key, "")))
        } else {
            // 重新抛出其他异常
            throw e
        }
    }

    /**
     * 处理从设备接收到的响应。
     * 当设备返回与已发送命令对应的数据时，应调用此方法。
     *
     * @param key 响应所属的设备标识符。
     * @param data 从设备接收到的响应数据，作为 JSON 字符串。
     */
    fun onDeviceResponse(key: String, data: String) {
        val command = sentCommandObjects[key] ?: "" // 检索原始命令
        // 向相应的待处理 Flow 发射成功结果
        pendingFlows[key]?.tryEmit(CommandResult(CommandSuccess(key, command, data), null))
    }

    /**
     * 取消特定设备的命令监听。
     * 这将删除给定设备键的所有待处理命令信息。
     *
     * @param key 要取消命令的设备标识符。
     */
    fun cancelCommand(key: String) {
        sentCommandObjects.remove(key)
        sentCommandTypes.remove(key)
        pendingFlows.remove(key)
    }

    /**
     * 清理所有资源，有效取消所有待处理命令并清除所有存储的数据。
     */
    fun clear() {
        sentCommandObjects.clear()
        sentCommandTypes.clear()
        pendingFlows.clear()
    }

}

/**
 * [CommandResult] 的扩展函数，用于将字符串响应转换为指定的对象类型 [T]。
 * 此函数使用提供的 [JsonAdapter] 进行反序列化。
 *
 * @param T 响应要转换的目标类型。
 * @param Command 命令对象的类型。
 * @param adapter 用于将 JSON 字符串响应转换为类型 [T] 的 [JsonAdapter]。
 * @return 一个新的 [CommandResult]，其响应类型已更改为 [T]。如果转换失败，则 success 部分将为 null。
 */
inline fun <reified T, Command> CommandResult<String, Command>.toObject(adapter: JsonAdapter<T>): CommandResult<T, Command> {
    return when {
        this.success != null -> {
            val responseObject = try {
                adapter.fromJson(this.success.response)
            } catch (e: Exception) {
                null
            }
            CommandResult(
                success = responseObject?.let {
                    CommandSuccess(this.success.key, this.success.command, it)
                },
                failure = this.failure
            )
        }
        else -> CommandResult(null, this.failure)
    }
}

/**
 * [DeviceCommandManager] 的扩展函数，用于发送具有强类型命令对象并接收强类型响应对象的命令。
 *
 * 此函数处理命令对象到 JSON 的序列化以及响应 JSON 反序列化回指定的 [ReceiveType]。
 *
 * @param SendType 要发送的命令对象的类型。
 * @param ReceiveType 预期响应对象的类型。
 * @param key 设备标识符。
 * @param commandObject 要发送的命令对象。它将被序列化为 JSON。
 * @param timeout 等待响应的最长时间（毫秒）。
 * @return 一个发射 [CommandResult] 的 [Flow]，其中响应类型为 [ReceiveType]，命令类型为 [SendType]。
 */
inline fun <reified SendType, reified ReceiveType> DeviceCommandManager.sendCommandAsFlowWithObject(
    key: String,
    commandObject: SendType,
    timeout: Long
): Flow<CommandResult<ReceiveType, SendType>> {
    // 将原始命令对象保存为 JSON 字符串
    val jsonString = moshi.adapter(SendType::class.java).toJson(commandObject)
    sentCommandObjects[key] = jsonString
    sentCommandTypes[key] = SendType::class.java.simpleName

    // 调用基础的 sendCommandAsFlow 发送 JSON 字符串
    val originalFlow = sendCommandAsFlow(key, jsonString, timeout)

    return flow {
        originalFlow.collect { result ->
            when {
                result.success != null -> {
                    try {
                        // 尝试将响应字符串反序列化为 ReceiveType
                        val responseObject = moshi.adapter(ReceiveType::class.java).fromJson(result.success.response)
                        if (responseObject != null) {
                            // 如果反序列化成功，则发射成功结果
                            emit(CommandResult(
                                success = CommandSuccess(key, commandObject, responseObject),
                                failure = null
                            ))
                        } else {
                            // 如果反序列化结果为 null，则发射失败
                            emit(CommandResult(
                                success = null,
                                failure = CommandFailure(key, commandObject)
                            ))
                        }
                    } catch (e: Exception) {
                        // 如果反序列化过程中发生异常，则发射失败
                        emit(CommandResult(
                            success = null,
                            failure = CommandFailure(key, commandObject)
                        ))
                    }
                }
                result.failure != null -> {
                    // 如果原始 Flow 发射了失败，则使用原始命令对象传播失败
                    emit(CommandResult(null, CommandFailure(key, commandObject)))
                }
                else -> {
                    // 不应该发生，但处理空结果的情况
                    emit(CommandResult(null, null))
                }
            }
        }
    }
}